/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.unifor.redes1.serial;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.UnsupportedCommOperationException;
import sun.misc.CRC16;

/**
 *
 * @author pedro
 */
public class SendArq extends Thread {

    Enumeration portList;
    CommPortIdentifier portId;
    SerialPort serialPort;
    OutputStream outputStream;
    boolean outputBufferEmptyFlag = false;
    int bytesRead;
    private int bufferSize;
    /**
     * Array of bytes
     */
    private File f;

    public SendArq(File f) {
        this.f = f;
        this.init();
        bytesRead = 0;
    }

    public SendArq(File f, int bufferSize) {
        this.f = f;
        this.init();
        bytesRead = 0;
        this.bufferSize = bufferSize;
    }

    @Override
    public void run() {
        FileInputStream fIS;
        byte[] buffer;
        Vector<Byte> dataAux = null;
        Vector data = new Vector();
        try {
            fIS = new FileInputStream(this.f);
            buffer = new byte[bufferSize];
            int r = 0;
            while ((r = fIS.read(buffer)) != -1) {
                dataAux = new Vector<Byte>();
                for (int j = 0; j < r; j++) {
                    bytesRead++;
                    dataAux.add(buffer[j]);
                }
                data.add(dataAux);
            }
            System.out.println("Total de Pacotes = " + data.size());
            System.out.println("Total de Bytes Lidos = " + bytesRead);
            if ((data.size() - 1) > 255) {
                System.out.println("Limite de Pacotes Exedido, configurando atutomaticamente para 255");
                this.bufferSize = (bytesRead / 255) + 1;
                System.out.println("Novo tamanho do Buffer = " + this.bufferSize);
                fIS = new FileInputStream(this.f);
                buffer = new byte[bufferSize];
                data = new Vector();
                this.bytesRead = 0;
                r = 0;
                while ((r = fIS.read(buffer)) != -1) {
                    dataAux = new Vector<Byte>();
                    for (int j = 0; j < r; j++) {
                        bytesRead++;
                        dataAux.add(buffer[j]);
                    }
                    data.add(dataAux);
                }
            }
            for (int i = 0; i < data.size(); i++) {
                Vector<Byte> pack = this.mountPackage((Vector) data.get(i), i, data.size(), 1, 2, f.getName());
                byte[] packEnvio = new byte[pack.size()];
                for (int j = 0; j < pack.size(); j++) {
                    Byte aux = pack.get(j);
                    packEnvio[j] = aux.byteValue();
                    System.out.println("[Montando Pacote ID = " + i + "] Adicioando Byte: " + aux.byteValue());
                    aux = null;
                }
                outputStream.write(packEnvio);
                Thread.sleep(100);
            }
            outputStream.flush();
            serialPort.close();
            outputStream.close();

        } catch (InterruptedException ex) {
            Logger.getLogger(SendArq.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(SendArq.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    //this.mountPackage(dataAux, packageId,1, 2, f.getName());
    private Vector<Byte> mountPackage(Vector<Byte> data, int id, int numPacks, int source, int destin, String name) {
        Vector<Byte> pack = new Vector<Byte>();
        // Byte SYN
        pack.add(new Byte((byte) 0xF1));
        // Byte SOH
        pack.add(new Byte((byte) 0xF2));
        // Byte de ID do Pacote
        pack.add(new Byte((byte) id));
        // Byte de Endereco de Origem
        pack.add(new Byte((byte) source));
        // Byte de Endereco de Destino
        pack.add(new Byte((byte) destin));
        // Bytes de nome do arquivo, se for o 1o pacote
        if (id == 0) {
            byte[] fileName = name.getBytes();
            if (fileName.length <= 255) {
                for (int i = 0; i < fileName.length; i++) {
                    if (fileName[i] == (byte) 0xFA) {
                        pack.add(new Byte((byte) 0xFA));
                    }
                    pack.add(new Byte(fileName[i]));
                }
            } else {
                for (int i = 0; i < 255; i++) {
                    // Adicionar o DLE falso nos dados do Nome do Arquivo
                    if (fileName[i] == (byte) 0xFA) {
                        pack.add(new Byte((byte) 0xFA));
                    }
                    pack.add(new Byte(fileName[i]));
                }
            }

        }
        //Bytes DLE-STX
        pack.add(new Byte((byte) 0xFA));
        pack.add(new Byte((byte) 0xF3));
        //Bytes da area de dados
        for (int k = 0; k < data.size(); k++) {
            // Adicionar o DLE falso nos dados
            if (data.get(k).byteValue() == (byte) 0xFA) {
                pack.add(new Byte((byte) 0xFA));
            }
            pack.add(data.get(k));
        }
        // Bytes DLE-ETX, se for o ultimo pacote
        if (id == numPacks - 1) {
            pack.add(new Byte((byte) 0xFA));
            pack.add(new Byte((byte) 0xF5));
        } // Bytes DLE-ETB, se for nao o ultimo pacote
        else {
            pack.add(new Byte((byte) 0xFA));
            pack.add(new Byte((byte) 0xF4));
        }
        // Byte PAD
        pack.add(new Byte((byte) 0xF6));

        CRC16 crc = new CRC16();
        // Calculo do BCC
        for (int i = 0; i < pack.size(); i++) {
//            System.err.println("ANTES DO CRC: " + pack.get(i));
            byte teste = (pack.get(i).byteValue());
            crc.update(teste);
        }
        // Adiciona o resultado do BCC na posicao correta do pacote
        byte[] crcArray = this.converteIntToByte(crc.value);
        // Remove o Byte PAD temporatiamente
        pack.remove(pack.size() - 1);
        for (int w = 0; w < crcArray.length; w++) {
            pack.add(new Byte(crcArray[w]));
        }
        crc.reset();
        // Rediciona o Byte PAD
        pack.add(new Byte((byte) 0xF6));
//        for (int i = 0; i < pack.size(); i++) {
//            System.err.println("DEPOIS DO CRC: " + pack.get(i));
//        }
        return pack;
    }

    private void init() {
        boolean portFound = false;
        String defaultPort = "COM9";
        //String defaultPort = "/dev/tty9";

        portList =
                CommPortIdentifier.getPortIdentifiers();
        while (portList.hasMoreElements()) {
            portId = (CommPortIdentifier) portList.nextElement();
            System.out.println(portId.getName());
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {

                if (portId.getName().equals(defaultPort)) {
                    System.out.println("Found port " + defaultPort);

                    portFound =
                            true;

                    try {
                        serialPort =
                                (SerialPort) portId.open("SimpleWrite", 2000);
                    } catch (PortInUseException e) {
                        System.out.println("Port in use.");
                        continue;

                    }



                    try {
                        outputStream = serialPort.getOutputStream();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    try {
                        serialPort.setSerialPortParams(9600,
                                SerialPort.DATABITS_8,
                                SerialPort.STOPBITS_1,
                                SerialPort.PARITY_NONE);
                    } catch (UnsupportedCommOperationException e) {
                        e.printStackTrace();
                    }
//                    try {
//                        serialPort.notifyOnOutputEmpty(true);
//                    } catch (Exception e) {
//                        System.out.println("Error setting event notification");
//                        System.out.println(e.toString());
//                        System.exit(-1);
//                    }
                }
            }
        }

        if (!portFound) {
            System.out.println("port " + defaultPort + " not found.");
        }
    }

    public byte[] converteIntToByte(int a) {
        byte[] ret = null;
        ret = new byte[2];
        ret[0] = (byte) (a / 256);
        ret[1] = (byte) (a % 256);
        return ret;
    }
}
